/**
 * Trabajo práctico 1 de 66.20 Organización de Computadoras.
 *
 * Veronica Bukaczewski
 * Ariel George
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include <limits.h>
#include "funciones.h"

const char* nombre_programa;

void print_version() {
    fprintf(stdout, "%s diff (GNU diffutils) 1.0\n", nombre_programa);
    fprintf(stdout, "Copyright (C) 2011 Free Software Foundation, Inc.\n");
    fprintf(stdout, "Writen by Veronica Bukaczewski y Ariel George.\n");
}

void print_help() {
    fprintf(stdout, "Usage: \n");
    fprintf(stdout, "  common -h\n");
    fprintf(stdout, "  common -V\n");
    fprintf(stdout, "  common [options] M N\n");
    fprintf(stdout, "Options:\n");
    fprintf(stdout, "  -h, --help      Prints usage information and quit.\n");
    fprintf(stdout, "  -V, --version   Print version information and quit.\n");
    fprintf(stdout, "  -o, --output    Path the output file.\n");
    fprintf(stdout, "  -d, --divisor   Just the divisor.\n");
    fprintf(stdout, "  -m, --multiple  Just the multiple.\n");
    fprintf(stdout, "Examples:\n");
    fprintf(stdout, "  common -o - 256 192\n");
}

void print_error() {
    fprintf(stderr, "%s: Try «%s --help» for more information.\n",
            nombre_programa, nombre_programa);
}

void print_result(unsigned int result, FILE* output) {
    fprintf(output, "%u\n", result);
}

int main(int argc, char **argv) {
    /* Guardar el nombre del programa */
    nombre_programa = argv[0];

    //Parseo de opciones
    /* Una cadena que lista las opciones cortas válidas */
    const char* const op_cortas = "hVo:dm";

    /* Una estructura de varios arrays describiendo los valores largos */
    const struct option op_largas[] = { { "help",0, NULL, 'h' }, { "version", 0, NULL, 'V' }, 
        { "output", 1, NULL, 'o' }, { "divisor", 0, NULL, 'd' }, { "multiple", 0, NULL, 'm' }, 
        { NULL, 0, NULL, 0 } };

    /* Llamamos a la función getopt */
    int c;
    int flag_m = 0;
    int flag_d = 0;
    FILE* output = stdout;
    while ((c = getopt_long(argc, argv, op_cortas, op_largas, NULL)) != -1) {
        switch (c) {
            case 'h': /* -h o --help */
                print_help();
                return EXIT_SUCCESS;
            case 'V': /* -V o --version */
                print_version();
                return EXIT_SUCCESS;
            case 'o': /* -o o --output */
                if(strcmp(optarg, "-") != 0)	
                    output = fopen(optarg, "w");
                if (!output) {
                    perror("Wrong output");
                    return EXIT_FAILURE;
                }
                break;
            case 'd': /* -d o --divisor */
                /* MCD */
                flag_d = 1;
                break;
            case 'm': /* -m o --multiple */
                /* mcm */
                flag_m = 1;
                break;
            case '?': /* opción no valida */
                print_error();
                return EXIT_FAILURE;
            case -1: /* No hay más opciones */
                break;
            default:
                return EXIT_FAILURE;//inesperado
        }
    } 

    /* Checkeo la cantidad minima de parametros */
    if(argc < 3) {
        print_error();
        return EXIT_FAILURE;  
    }

    unsigned int M,N;
    N = atoi(argv[--argc]);
    M = atoi(argv[--argc]);

    /* Checkeo rango de los argumentos */
    if(!(M>=2 && M<=INT_MAX) || !(N>=2 && N<=INT_MAX)) {
        perror("The arguments must be between [2, MAXINT]");
        return EXIT_FAILURE;
    }

    if(!flag_m && !flag_d){
        flag_m=1;
        flag_d=1;
    }

    /* Common */
    if(flag_d)
        print_result(mcd(M,N), output);
    if(flag_m) {
        unsigned result = mcm(M, N);
        if (result != OVERFLOW && result != ERROR)
            print_result(result, output);
        else
            perror("Overflow");
    }

    return EXIT_SUCCESS;
}
